from common.db import models
from common.db.models import Choices
from datetime import datetime, time
from django.contrib.postgres.fields import JSONField
from decimal import Decimal

from apps.storage.models import PublicFile
from utils import weekday_strs
from django.db.models import Sum

class TunLevel(models.Model):
    """
    囤等级
    小白菜囤家：成长值小于50点

    V1灰铁囤家：成长值满50点
                 1. 每年提货包邮一次

    V2青铜囤家：成长值满200点
                 1. 每年提货包邮三次
                 2. 免费获得试饮装一份

    V3白银囤家：成长值满500点
                1. 每年提货包邮五次
                2. 免费获得试饮装两份
                3. 每年可免费在线下体验体品茶活动一次


    """

    class Meta:
        ordering = ('need_grow_coin',)
        verbose_name = verbose_name_plural = '囤级'

    init_data = [
        {"name": "小白菜", "need_grow_coin": 0},
        {"name": "灰铁", "need_grow_coin": 50},
        {"name": "青铜", "need_grow_coin": 200},
        {"name": "白银", "need_grow_coin": 500},
        {"name": "黄金", "need_grow_coin": 1000},
        {"name": "铂金", "need_grow_coin": 2000},
        {"name": "钻石", "need_grow_coin": 5000},
        {"name": "最强囤主", "need_grow_coin": 20000},
        {"name": "殿堂囤主", "need_grow_coin": 50000},
    ]

    name = models.CharField("等级名")
    icon_public_file_id = models.CharField("PublicFileID", blank=True, null=True)
    need_grow_coin = models.IntegerField("需要成长值")

    def __str__(self):
        return '{name}:{need_grow_coin}'.format(name=self.name, need_grow_coin=self.need_grow_coin)

    @property
    def level_sort(self):
        level_num = 0
        if self.need_grow_coin == 0:
            level_num = 0
        elif self.need_grow_coin == 50:
            level_num = 1
        elif self.need_grow_coin == 200:
            level_num = 2
        elif self.need_grow_coin == 500:
            level_num = 3
        elif self.need_grow_coin == 1000:
            level_num = 4
        elif self.need_grow_coin == 2000:
            level_num = 5
        elif self.need_grow_coin == 5000:
            level_num = 6
        elif self.need_grow_coin == 20000:
            level_num = 7
        elif self.need_grow_coin == 50000:
            level_num = 8
        return level_num

    @property
    def next_tun_level(self):
        return TunLevel.objects.filter(need_grow_coin__gt=self.need_grow_coin).order_by('need_grow_coin').first()

    def next_level_beneficial(self):
        take_out_free_postage_times_1y = 0  # 每年提货包邮次数(负数代表永久)
        free_get_try_drink_count = 0  # 免费获得试饮装份数
        free_offline_experience_times_1y = 0  # 每年免费线下体验次数
        procedure_preferential_percent = 0  # 手续费减免比例(EG:0.005)
        repo_preferential_percent = 0  # 仓储费减免比例(EG:0.015)
        first_publish_product_pre_buy_times_1y = 0  # 每年首发产品优先囤茶次数

        if self.need_grow_coin == 50:
            """V1灰铁囤家：成长值满50点
                 1. 每年提货包邮一次"""
            take_out_free_postage_times_1y = 1

        elif self.need_grow_coin == 200:
            """V2青铜囤家：成长值满200点
              1.每年提货包邮三次
              2.免费获得试饮装一份"""
            take_out_free_postage_times_1y = 2  # 升級+2
            free_get_try_drink_count = 1

        elif self.need_grow_coin == 500:
            """ V3白银囤家：成长值满500点
                    1. 每年提货包邮五次  +2
                    2. 免费获得试饮装两份
                    3. 每年可免费在线下体验体品茶活动一次"""
            take_out_free_postage_times_1y = 5
            free_get_try_drink_count = 2
            free_offline_experience_times_1y = 1

        elif self.need_grow_coin == 1000:
            """
            V4黄金囤家：成长值满1000点
                1. 每年提货包邮五次 +0
                 2. 免费获得试饮装五份 +3
                 3. 每年可免费在线下体验体品茶活动三次 +2
                 4. 每笔交易手续费减免0.5%
            """
            take_out_free_postage_times_1y = 5
            free_get_try_drink_count = 5
            free_offline_experience_times_1y = 3
            procedure_preferential_percent = 0.005

        elif self.need_grow_coin == 2000:
            """
            V5铂金囤家：成长值满2000点
                        1. 每年提货包邮六次 +1
                          2. 免费获得试饮装八份 +3
                          3. 每年可免费在线下体验体品茶活动六次 +3
                          4. 每笔交易手续费减免0.5% +0
                          5. 每笔库存费减免5% +0.05
            """
            take_out_free_postage_times_1y = 6
            free_get_try_drink_count = 8
            free_offline_experience_times_1y = 6
            procedure_preferential_percent = 0.005
            repo_preferential_percent = 0.05

        elif self.need_grow_coin == 5000:
            """
            V6钻石囤家：成长值满5000点
                        1. 每年提货包邮六次 +0
                      2. 免费获得试饮装八份  +0 
                      3. 每年可免费在线下体验体品茶活动六次 +0
                      4. 每笔交易手续费减免1% +0.005
                      5. 每笔库存费减免10% + 0.05
            """
            take_out_free_postage_times_1y = 6
            free_get_try_drink_count = 8
            free_offline_experience_times_1y = 6
            procedure_preferential_percent = 0.01
            repo_preferential_percent = 0.1

        elif self.need_grow_coin == 20000:
            """
            V7最强囤主：成长值满20000点
            1. 每年提货包邮十次  +4
            2. 免费获得试饮装十份 +2
            3. 每年可免费在线下体验体品茶活动十次 +4
            4. 每笔交易手续费减免1% +0
            5. 每笔库存费减免15% + 0.05
            6. 每年平台首发产品有两次优先囤茶权（提前1小时） +2
            """
            take_out_free_postage_times_1y = 10
            free_get_try_drink_count = 10
            free_offline_experience_times_1y = 10
            procedure_preferential_percent = 0.01
            repo_preferential_percent = 0.15
            first_publish_product_pre_buy_times_1y = 2

        elif self.need_grow_coin == 50000:
            from apps.product.models import Product
            """
            V8殿堂囤主：成长值满50000点
              1. 提货永久包邮   +负
              2. 每一款首发产品，均可免费获得试饮装1份   +产品
              3. 每年可免费在线下体验体品茶活动十次 +0
              4. 每笔交易手续费减免1% +0
              5. 每笔库存费减免20% +0.05
              6. 每年平台首发产品有五次优先囤茶权（提前1小时） +3
            """
            take_out_free_postage_times_1y = -100
            free_get_try_drink_count = Product.objects.filter(status=Product.ProductStatus.PRE_SALE_ING.name).count()
            free_offline_experience_times_1y = 10
            procedure_preferential_percent = 0.01
            repo_preferential_percent = 0.2
            first_publish_product_pre_buy_times_1y = 5

        return {
            "take_out_free_postage_times_1y": take_out_free_postage_times_1y,
            "free_get_try_drink_count": free_get_try_drink_count,
            "free_offline_experience_times_1y": free_offline_experience_times_1y,
            "procedure_preferential_percent": procedure_preferential_percent,
            "repo_preferential_percent": repo_preferential_percent,
            "first_publish_product_pre_buy_times_1y": first_publish_product_pre_buy_times_1y,
        }


class User(models.Model):
    """User"""

    class Gender(Choices):
        MALE = "男"
        FEMALE = "女"
        SECRET = '保密'

    class CertificateType(Choices):
        CARD = "身份证"
        OTHER = "其他"

    class CertificateState(Choices):
        VERIFIED = "已实名认证"
        UNVERIFIED = "未实名认证"

    class State(Choices):
        NORMAL = "正常"
        LOCKED = "锁定"
        INACTIVE = "未激活"

    class Source(Choices):
        SELF_REG = "自注册"
        BE_GIFTED = "受赠"  # 被赠送时创建
        PRE_CREATED = "预置"  # 预先创建

    class DeviceType(Choices):
        IOS = 'IOS'
        ANDROID = "安卓"

    class Meta:
        verbose_name = verbose_name_plural = '用户'

    nickname = models.CharField(max_length=30, default='', verbose_name="昵称", blank=True, null=True)
    gender = models.CharField(max_length=6, choices=Gender.get_choices(), verbose_name="性别", default=Gender.SECRET.name)
    mobile = models.CharField(max_length=50, verbose_name="电话", unique=True)
    real_name = models.CharField(max_length=30, default='', verbose_name="真实姓名", blank=True, null=True)
    certificate_type = models.CharField(choices=CertificateType.get_choices(), default=CertificateType.CARD.name,
                                        max_length=20, verbose_name="证件类型")
    certificate_no = models.CharField(default='', max_length=20, verbose_name=u'证件号码', blank=True, null=True)
    certificate_state = models.CharField(choices=CertificateState.get_choices(), max_length=20, verbose_name="实名制状态",
                                         default=CertificateState.UNVERIFIED.name)
    avatar_public_file_id = models.CharField("头像PublicFileID", blank=True, null=True)
    goods_block_count = models.IntegerField(default=0, verbose_name="囤茶数量")
    trade_num = models.IntegerField(default=0, verbose_name="交易数量")
    real_time_income = models.DecimalField("存仓量实时收益", max_digits=10, decimal_places=2, blank=True, null=True)
    last_login_time = models.DateTimeField(default=datetime.now, verbose_name="最后一次登录时间")
    today_total_value_data = JSONField("日实时价值", default={"points": []})
    current_week_total_value_data = JSONField("周实时价值", default={"points": []})
    current_month_total_value_data = JSONField("月实时价值", default={"points": []})
    state = models.CharField(choices=State.get_choices(), default=State.NORMAL.name, max_length=20, verbose_name="用户状态")
    balance = models.DecimalField("余额", max_digits=10, decimal_places=2, default=Decimal("0.00"))
    grow_coin = models.IntegerField("成长值", default=0)
    tun_level = models.ForeignKey(TunLevel, blank=True, null=True)
    open_repo = models.BooleanField("是否公开茶仓", default=True)
    source = models.CharField("用户来源", choices=Source.get_choices(), default=Source.SELF_REG.name)
    #
    device_type = models.CharField("设备类型", choices=DeviceType.get_choices(), default=DeviceType.IOS.name)
    device_token = models.CharField("设备Token", blank=True, null=True)
    getui_clientid = models.CharField("个推ClientID", blank=True, null=True)

    is_vip = models.BooleanField(verbose_name='是否是VIP', default=False)
    # x新增
    purse_balance = models.DecimalField("积分余额", max_digits=10, decimal_places=2, default=Decimal("0.00"))
    purse_password = models.CharField("钱包密码", blank=True, null=True)
    purse_address = models.CharField(max_length=30, verbose_name="钱包地址")

    @classmethod
    def get_ignore_fields(cls):
        return super().get_ignore_fields() + ['today_total_value_data', 'current_week_total_value_data', 'current_month_total_value_data']

    @property
    def line_data_count(self):
        tcount = len(self.today_total_value_data.get('points', []))
        wcount = len(self.current_week_total_value_data.get('points', []))
        mcount = len(self.current_month_total_value_data.get('points', []))
        return '%d-%d-%d' % (tcount, wcount, mcount)

    def get_pre_sale_order_limit(self, product_id):
        from apps.product.models import Product
        from tuntun.settings import NORMAL_USER_PRE_SALE_ORDER_LIMIT
        p = Product.objects.filter(pk=product_id).first()  # type: Product
        if not p:
            return 0
        if self.is_vip:
            return p.publish_count if p.publish_count <= 300 else p.publish_count
        else:
            return NORMAL_USER_PRE_SALE_ORDER_LIMIT

    def __str__(self):
        return self.mobile

    @property
    def avatar_public_file(self) -> PublicFile:
        return PublicFile.objects.filter(id=self.avatar_public_file_id).first()

    @property
    def avatar(self):
        return self.avatar_public_file.url if self.avatar_public_file else ''

    @classmethod
    def process_points(self, points, keep_count=6):
        from django.core.paginator import Paginator
        print('points', points)
        if len(points) == 0:
            return []
        per_page = len(points) // keep_count + 1 if len(points) // keep_count == 0 else len(points) // keep_count
        pag = Paginator(points, per_page)
        new_points = []
        for i in range(pag.num_pages):
            heap = [float(p['value']) for p in pag.page(i+1).object_list]
            avg = round(sum(heap)/len(heap), 2)
            new_points.append({'value': str(avg)})
            if len(new_points) == keep_count:
                break
        return new_points

    def get_tea_repo_statistics(self):
        from apps.product.models import GoodsBlock, Product
        from apps.order.models import SubOrder, Order
        from django.db.models import Q
        user_blocks = GoodsBlock.objects.filter(owner_user_id=self.id, taken_out=False)  # type: list[GoodsBlock]

        in_repo_total_value = user_blocks.aggregate(s=Sum('trade_price'))
        in_repo_total_value = in_repo_total_value['s'] if in_repo_total_value else Decimal('0.00')
        in_repo_total_value = in_repo_total_value if in_repo_total_value else Decimal('0.00')
        now_total_value = user_blocks.aggregate(s=Sum('product__last_trade_price'))
        now_total_value = now_total_value['s'] if now_total_value else Decimal('0.00')
        now_total_value = now_total_value if now_total_value else Decimal('0.00')
        self.goods_block_count = user_blocks.count()
        self.save()
        ttvd = self.today_total_value_data
        cwtvd = self.current_week_total_value_data
        cmtvd = self.current_month_total_value_data
        #
        percent = ((now_total_value - in_repo_total_value) / in_repo_total_value) if in_repo_total_value != Decimal(
            '0.00') else 0
        return {
            "nickname": self.nickname,
            "avatar": self.avatar,
            "open_repo": self.open_repo,
            "in_repo_total_value": in_repo_total_value.__str__(),
            "now_total_value": now_total_value.__str__(),
            "trade_count": self.trade_num.__str__(),
            "today_total_value_data": ttvd,
            "current_week_total_value_data": cwtvd,
            "current_month_total_value_data": cmtvd,
            "goods_block_count": self.goods_block_count.__str__(),
            "total_value_up_down_percent": (round(percent, 3) * 100).__str__(),
        }

    @classmethod
    def get_nickname(self):
        from random import choice
        """
        tun+2位随机数+4位随机数带字母
        :return:
        """
        seeds = "1234567890abcdefghijkmnopqrstuvwxyz"
        random_str = []
        for i in range(4):
            random_str.append(choice(seeds))

        seeds1 = "1234567890"
        random_str1 = []
        for i in range(2):
            random_str1.append(choice(seeds1))

        str = "".join(random_str)
        str1 = "".join(random_str1)

        return "tun" + str1 + str

    def user_info(self):
        """用戶信息"""
        return {
            "uid": self.id.__str__(),
            "mobile": self.mobile.__str__(),
            "nickname": self.nickname.__str__(),
            "real_name": self.real_name.__str__(),
            "certificate_type": self.certificate_type.__str__(),
            "certificate_type_map": dict(User.CertificateType.get_choices()),
            "certificate_no": self.certificate_no.__str__(),
            "certificate_state": self.certificate_state.__str__(),
            "certificate_state_map": dict(User.CertificateState.get_choices()),
            "logo": self.avatar,
            "goods_block_count": self.goods_block_count.__str__(),
            "trade_num": self.trade_num.__str__(),
            "real_time_income": self.real_time_income.__str__(),
            "today_total_value_data": self.today_total_value_data,
            "current_week_total_value_data": self.current_week_total_value_data,
            "current_month_total_value_data": self.current_month_total_value_data,
            "balance": self.balance.__str__(),
            "grow_coin": self.grow_coin.__str__(),
            "tun_level": self.tun_level.name.__str__(),
            "source": self.source,
            "source_map": dict(User.Source.get_choices())
        }

    def current_tun_level(self):
        """用戶刷新当前等级"""
        import time
        print(1)
        tun_level = TunLevel.objects.filter(need_grow_coin__lte=self.grow_coin).order_by('-need_grow_coin').first()
        next_tun_level = tun_level.next_tun_level
        if next_tun_level:
            print(2)
            next_level_beneficial = next_tun_level.next_level_beneficial()
            take_out_free_postage_times_1y = int(
                next_level_beneficial['take_out_free_postage_times_1y'])  # 每年提货包邮次数(负数代表永久)
            free_get_try_drink_count = int(next_level_beneficial['free_get_try_drink_count'])  # 免费获得试饮装份数
            free_offline_experience_times_1y = int(next_level_beneficial['free_offline_experience_times_1y'])  # 每年免费线下体验次数
            procedure_preferential_percent = Decimal(
                next_level_beneficial['procedure_preferential_percent'])  # 手续费减免比例(EG:0.005)
            repo_preferential_percent = Decimal(next_level_beneficial['repo_preferential_percent'])  # 仓储费减免比例(EG:0.015)
            first_publish_product_pre_buy_times_1y = next_level_beneficial[
                'first_publish_product_pre_buy_times_1y']  # 每年首发产品优先囤茶次数

            next_tun_level_distance = next_tun_level.need_grow_coin - self.grow_coin
            if tun_level != self.tun_level:  # 升级权限
                print(3)
                self.tun_level = tun_level
                self.save()
                user_level_right = UserTunLevelRight.objects.filter(user_id=self.id).first()
                if user_level_right:
                    print(4)
                    user_level_right.tun_level_id = tun_level.id
                    user_level_right.take_out_free_postage_times_1y += take_out_free_postage_times_1y
                    user_level_right.free_get_try_drink_count += free_get_try_drink_count
                    user_level_right.free_offline_experience_times_1y += free_offline_experience_times_1y
                    user_level_right.procedure_preferential_percent += procedure_preferential_percent
                    user_level_right.repo_preferential_percent += repo_preferential_percent
                    user_level_right.first_publish_product_pre_buy_times_1y += first_publish_product_pre_buy_times_1y
                    user_level_right.save()
                else:
                    print(5)
                    user_level_right = UserTunLevelRight.objects.create(
                        user_id=self.id,
                        tun_level_id=tun_level.id,
                        take_out_free_postage_times_1y=take_out_free_postage_times_1y,
                        free_get_try_drink_count=free_get_try_drink_count,
                        free_offline_experience_times_1y=free_offline_experience_times_1y,
                        procedure_preferential_percent=procedure_preferential_percent,
                        repo_preferential_percent=repo_preferential_percent,
                        first_publish_product_pre_buy_times_1y=first_publish_product_pre_buy_times_1y
                    )
                # 更新权益记录
                if user_level_right.take_out_free_postage_times_1y > 0:
                    print(6)
                    # 提仓包邮权益
                    rest_times = UserTunLevelRightTimes.objects.filter(user=self,
                                                                       right=UserTunLevelRightTimes.Right.TAKE_OUT_FREE_POSTAGE.name,
                                                                       used=False, expire_at__gt=time.time()).count()
                    for i in range(user_level_right.take_out_free_postage_times_1y - rest_times):
                        UserTunLevelRightTimes.objects.create(user=self,
                                                              right=UserTunLevelRightTimes.Right.TAKE_OUT_FREE_POSTAGE.name,
                                                              used=False, expire_at=time.time() + 365 * 24 * 60 * 60)
                if user_level_right.free_get_try_drink_count > 0:
                    # 获得试饮装权益
                    rest_times = UserTunLevelRightTimes.objects.filter(user=self,
                                                                       right=UserTunLevelRightTimes.Right.FREE_GET_TRY_DRINK.name,
                                                                       used=False, expire_at__gt=time.time()).count()
                    for i in range(user_level_right.free_get_try_drink_count - rest_times):
                        UserTunLevelRightTimes.objects.create(user=self,
                                                              right=UserTunLevelRightTimes.Right.FREE_GET_TRY_DRINK.name,
                                                              used=False, expire_at=time.time() + 365 * 24 * 60 * 60)
                if user_level_right.free_offline_experience_times_1y > 0:
                    # 线下体验权益
                    rest_times = UserTunLevelRightTimes.objects.filter(user=self,
                                                                       right=UserTunLevelRightTimes.Right.FREE_OFFLINE_EXPERIENCE.name,
                                                                       used=False, expire_at__gt=time.time()).count()
                    for i in range(user_level_right.free_offline_experience_times_1y - rest_times):
                        UserTunLevelRightTimes.objects.create(user=self,
                                                              right=UserTunLevelRightTimes.Right.FREE_OFFLINE_EXPERIENCE.name,
                                                              used=False, expire_at=time.time() + 365 * 24 * 60 * 60)
                if user_level_right.first_publish_product_pre_buy_times_1y > 0:
                    # 首发产品提前购买预售权益
                    rest_times = UserTunLevelRightTimes.objects.filter(user=self,
                                                                       right=UserTunLevelRightTimes.Right.FIRST_PUBLISH_PRODUCT_PRE_BUY.name,
                                                                       used=False, expire_at__gt=time.time()).count()
                    for i in range(user_level_right.first_publish_product_pre_buy_times_1y - rest_times):
                        UserTunLevelRightTimes.objects.create(user=self,
                                                              right=UserTunLevelRightTimes.Right.FIRST_PUBLISH_PRODUCT_PRE_BUY.name,
                                                              used=False, expire_at=time.time() + 365 * 24 * 60 * 60)
            return {
                "tun_level_name": tun_level.name,
                "grow_coin": self.grow_coin,
                "next_tun_level_distance": next_tun_level_distance,
            }
        else:
            return {
                "tun_level_name": tun_level.name,
                "grow_coin": self.grow_coin,
                "next_tun_level_distance": 0,
            }

    def get_user_level_right(self):
        return UserTunLevelRight.objects.filter(user_id=self.id).first()

    @property
    def sign_count(self):
        return Sign.objects.filter(user_id=self.id).count().__str__()

    class Meta:
        verbose_name = '用户信息'
        verbose_name_plural = verbose_name


class UserKeepValue(models.Model):
    user = models.ForeignKey(User, verbose_name="用户")
    datetime_point = models.DateTimeField(verbose_name='时间点')
    kind = models.CharField(verbose_name='种类')
    keep_info = JSONField(verbose_name='持仓数据', default={})
    total_value = models.DecimalField('价值', max_digits=10, decimal_places=2)

    @classmethod
    def get_current_datetime_point(cls, kind):
        import datetime
        day_parts = [0, 4, 8, 12, 16, 20, 24]
        now = datetime.datetime.now()
        year, month, day, hour = now.year, now.month, now.day, now.hour
        week = now.isoweekday()

        if kind == 'T':
            current_day_hour = 0
            for i in day_parts:
                if hour >= i:
                    current_day_hour = i
                else:
                    break
            current_point_time = datetime.datetime(year, month, day, current_day_hour, 0, 0)
        elif kind == 'W':
            current_point_time = datetime.datetime(year, month, day, 0, 0, 0)
        else:
            current_point_time = datetime.datetime(year, month, day, 0, 0, 0) - datetime.timedelta(days=week-1)

        return current_point_time

    @classmethod
    def update_user_data(cls, user, kind):
        if kind == 'T':
            n = 6
        elif kind == 'W':
            n = 7
        else:
            n = 4
        objs = cls.objects.filter(user=user, kind=kind).order_by('-add_timestamp')[0:n]

        points = [{'datetime_point': obj.datetime_point.strftime('%Y-%m-%d %H:%M:%S %w'), 'value': obj.total_value.__str__()} for obj in objs][::-1]
        if len(points) == 0:
            points = [{'datetime_point': '0', 'value': '0'}]

        if kind == 'T':
            user.today_total_value_data = {'points': points}
        elif kind == 'W':
            user.current_week_total_value_data = {'points': points}
        else:
            user.current_month_total_value_data = {'points': points}
        user.save()
        return points

class UserDevice(models.Model):
    """
    设备表
    """

    class DeviceType(Choices):
        IOS = 'IOS'
        ANDROID = "安卓"

    user_id = models.CharField(max_length=32, default='', verbose_name="用户id", blank=True, null=True)
    device_type = models.CharField("设备类型", choices=DeviceType.get_choices(), default=DeviceType.IOS.name)
    device_token = models.CharField("设备Token", blank=True, null=True)
    getui_clientid = models.CharField("个推ClientID", blank=True, null=True)

    class Meta:
        verbose_name = "设备个推表"
        verbose_name_plural = verbose_name


class UserIncome(models.Model):
    """
    * UserRepoIncome（用户实时收益）
*** User
*** 收益
    """
    user = models.ForeignKey(User, verbose_name="用户")
    income = models.DecimalField(verbose_name="收益", max_digits=10, decimal_places=2)
    statistics_datetime = models.DateTimeField("统计时间")

    class Meta:
        verbose_name = "用户收益表"
        verbose_name_plural = verbose_name

    def __str__(self):
        return self.income


class UserTotalValue(models.Model):
    """
    User Total Value
    """

    class Meta:
        ordering = ['add_timestamp']

    user = models.ForeignKey(User, verbose_name='用户')
    total_value = models.DecimalField(verbose_name='总价值', max_digits=10, decimal_places=2)
    statistics_datetime = models.DateTimeField("统计时间")

    def __str__(self):
        return self.total_value

    @classmethod
    def get_line_data_for_user(cls, user, roll=True):
        from apps.product.models import GoodsBlock, Product
        from apps.order.models import SubOrder, Order
        from django.db.models import Q
        user_blocks = GoodsBlock.objects.filter(owner_user_id=user.id)  # type: list[GoodsBlock]

        taked_gb_ids = [so.goods_block_id for so in SubOrder.objects.filter(
            goods_block_id__in=[ub.id for ub in user_blocks],
            create_type=SubOrder.CreateType.TAKE_OUT.name,
            order__status=Order.Status.FINISHED.name
        )]
        user_blocks.exclude(id__in=taked_gb_ids)

        in_repo_total_value = Decimal("0")
        now_total_value = Decimal("0")
        user.goods_block_count = user_blocks.count()
        user.save()
        for ub in user_blocks:
            in_repo_total_value += ub.trade_price
            now_total_value += ub.product.last_trade_price

        day_parts = [0, 4, 8, 12, 16, 20]
        week_parts = [6,5,4,3,2,1,0]
        month_parts = [0, 7, 14, 21]
        import datetime, time
        now = datetime.datetime.now()
        year, month, day, hour = now.year, now.month, now.day, now.hour
        week = now.isoweekday()

        current_day_hour = 0
        for i in day_parts:
            if hour >= i:
                current_day_hour = i
            else:
                break
        today_last_point_time = datetime.datetime(year, month, day, current_day_hour, 0, 0)

        current_week_last_point_time = datetime.datetime(year, month, day, 0, 0, 0)

        current_month_last_point_time = datetime.datetime(year, month, day, 0, 0, 0) - datetime.timedelta(days=week-1)

        today_time_points = []
        week_time_points = []
        month_time_points = []

        for hp in day_parts[::-1]:
            tp = today_last_point_time - datetime.timedelta(hours=hp)
            if tp.day != now.day and not roll:
                continue
            today_time_points.append(tp)

        for i in week_parts:
            if i > week-1 and not roll:
                continue
            tp = current_week_last_point_time - datetime.timedelta(days=i)
            week_time_points.append(tp)

        for i in month_parts[::-1]:
            tp = current_month_last_point_time - datetime.timedelta(days=i)
            if tp.month != now.month and not roll:
                continue
            month_time_points.append(tp)

        today_data = {'points': []}
        week_data = {'points': []}
        month_data = {'points': []}

        for tp in today_time_points:
            ts = time.mktime(tp.timetuple())
            obj = cls.objects.filter(user=user, add_timestamp__lt=ts).order_by('-add_timestamp').first()
            if not obj:
                continue
            today_data['points'].append({'value': obj.total_value.__str__(), 'time': tp.strftime('%H')})

        for tp in week_time_points:
            ts = time.mktime(tp.timetuple())
            obj = cls.objects.filter(user=user, add_timestamp__lt=ts).order_by('-add_timestamp').first()
            if not obj:
                continue
            week_data['points'].append({'value': obj.total_value.__str__(), 'time': tp.isoweekday()})

        for tp in month_time_points:
            ts = time.mktime(tp.timetuple())
            obj = cls.objects.filter(user=user, add_timestamp__lt=ts).order_by('-add_timestamp').first()
            if not obj:
                continue
            month_data['points'].append({'value': obj.total_value.__str__(), 'time': tp.strftime('%Y-%m-%d')})

        if len(today_data['points']) < 6:
            today_data['points'].insert(0, {'value': in_repo_total_value.__str__(), 'time': '0'})
        if len(week_data['points']) < 7:
            week_data['points'].insert(0, {'value': in_repo_total_value.__str__(), 'time': '1'})
        if len(month_data['points']) < 4:
            month_data['points'].insert(0, {'value': in_repo_total_value.__str__(), 'time': '1'})

        return {
            'today_data': today_data,
            'week_data': week_data,
            'month_data': month_data,
        }


    def to_chart_value_point(self, fmt='d'):
        """
        :param fmt: d-每日 w-每周 m-每月
        """
        if fmt == 'd':
            dt_str = self.statistics_datetime.strftime('%H:%M')
        elif fmt == 'w':
            # print(self.statistics_datetime.isocalendar())
            dt_str = weekday_strs[self.statistics_datetime.isocalendar()[2] - 1]
        elif fmt == 'm':
            dt_str = self.statistics_datetime.day
        return {'datetime': dt_str, 'value': self.total_value.__str__()}


class WithdrawCash(models.Model):
    """
    用户提现
    """

    class Meta:
        verbose_name = verbose_name_plural = '提现记录'

    class AccountType(Choices):
        ALI = "支付宝"
        WEIXIN = "微信"
        BALANCE = "余额"
        PURSEBALANCE = "钱包余额"

    class State(Choices):
        STARTED = "已发起"
        FINISHED = "已完成"
        REJECTED = "已拒绝"
        FAILED = "失败"

    user = models.ForeignKey(User, verbose_name="用户")
    amount = models.DecimalField(verbose_name="提现金额", max_digits=8, decimal_places=2)
    to_account = models.CharField(max_length=50, verbose_name="提现账号")
    account_type = models.CharField(choices=AccountType.get_choices(), default=AccountType.ALI.name,
                                    max_length=20, verbose_name="提现方式")
    state = models.CharField(choices=State.get_choices(), max_length=20, verbose_name="提现状态",
                             default=State.STARTED.name)
    date = models.DateField("日期", default=datetime.today)
    provider_order_id = models.CharField(verbose_name='供应商订单ID', blank=True, null=True)
    provider_process_datetime = models.DateTimeField('供应商处理时间', blank=True, null=True)
    provider_response_data = models.TextField("供应商返回数据", blank=True)

    def as_dict(self):
        import time
        return {
            'amount': self.amount.__str__(),
            'time': time.strftime('%m-%d %H:%M', time.localtime(self.add_timestamp)),
            'info': self.info,
        }

    @property
    def info(self):
        if self.account_type == WithdrawCash.AccountType.ALI.name:
            return '提现到支付宝'
        elif self.account_type == WithdrawCash.AccountType.WEIXIN.name:
            return '提现到微信'
        else:
            return '-'

    @property
    def provider_response_msg(self):
        ppd = self.provider_response_data
        try:
            resp = eval(ppd)
            code = resp['code']
            sub_code = resp.get('sub_code', None)
            if int(code) == 10000:
                return '提现成功已到账'
            else:
                if sub_code == 'PAYER_BALANCE_NOT_ENOUGH':
                    return '请联系囤囤客服'
                return resp['sub_msg']
        except:
            return '未知'

    class Meta:
        verbose_name = "用户提现"
        verbose_name_plural = verbose_name

    def __str__(self):
        return self.to_account


class AuthCode(models.Model):
    """
    短信验证码
    * AuthCode （验证码）
    *** 手机号
    *** 验证码
    """
    mobile = models.CharField(max_length=11, verbose_name="电话")
    code = models.CharField(max_length=8, verbose_name="验证码")
    expire_at = models.FloatField("过期时间")

    class Meta:
        verbose_name = "短信验证码"
        verbose_name_plural = verbose_name
        ordering = ('-add_timestamp', )

    def __str__(self):
        return self.code


class Sign(models.Model):
    """
    * 签到
*** 用户
*** 签到时间
    """
    user = models.ForeignKey(User, verbose_name="用户")
    sign_datetime = models.DateTimeField("打卡时间")

    @property
    def today_sign(self):
        date_now = datetime.today().date()
        sign_date = self.sign_datetime.date()
        day = (sign_date - date_now).days
        if day == 0:
            return True
        else:
            return False


class AdministrativeArea(models.Model):
    """
    行政区域
    id integer NOT NULL,
    uuid character varying(32) NOT NULL,
    add_timestamp bigint NOT NULL,
    update_timestamp bigint NOT NULL,
    is_deleted boolean NOT NULL,
    level character varying(1024) NOT NULL,
    name character varying(1024) NOT NULL,
    code character varying(1024),
    parent_id integer
    """

    class Level(Choices):
        S = "国"
        P = "省"
        C = "市"
        X = "县"

    nid = models.IntegerField("数字ID")
    level = models.CharField("级别", choices=Level.get_choices())
    name = models.CharField("名字")
    zip_code = models.CharField(verbose_name='邮政编码')
    parent = models.ForeignKey("self", "父级", blank=True, null=True)
    parent_nid = models.IntegerField("父级nid", blank=True, null=True)

    def __str__(self):
        obj = self
        s_parts = ["%s%s" % (obj.name, getattr(AdministrativeArea.Level, obj.level).value)]
        while obj.parent:
            obj = obj.parent
            s_parts.append("%s%s" % (obj.name, getattr(AdministrativeArea.Level, obj.level).value))
        s_parts.reverse()
        return ''.join(s_parts)


class Address(models.Model):
    """
     * 地址
    *** 用户
    *** 地址
    *** 收件人
    *** 电话
    *** 是否默认
    """
    output_fields = ['id', 'name', 'mobile', 'full_address', 'is_default', 'address',
                     'administrative_area_info', 'administrative_area_name_info']

    user = models.ForeignKey(User, verbose_name="用户")
    # administrative_area = models.ForeignKey(AdministrativeArea, "行政区域", blank=True, null=True)
    administrative_area_info = JSONField(verbose_name='行政区域信息', default=[])
    administrative_area_name_info = JSONField(verbose_name='行政区域信息', default=[])
    address = models.CharField(max_length=100, default="", verbose_name="详细地址")
    name = models.CharField(max_length=100, default="", verbose_name="收件人姓名")
    mobile = models.CharField(max_length=11, default="", verbose_name="电话")
    is_default = models.BooleanField("是否默认", default=False)
    full_address = models.CharField("完整地址", blank=True, null=True)

    # @property
    # def administrative_area_nid(self):
    #     return self.administrative_area.nid

    class Meta:
        verbose_name = "地址"
        verbose_name_plural = verbose_name

    def __str__(self):
        return self.address


class GrowCoinRecord(models.Model):
    """
    成长值记录
    """

    class HappenType(models.Choices):
        REG = "注册"
        SIGN = "签到"
        TRADE = "交易"
        TUN = "囤茶"
        SHARE = "分享"
        INVITE = "邀请（推荐）"
        KEEP_SIGN_REWARD = "连续签到奖励"
        MO_MULTI_PRICE = "挂单价为1倍以上"

    class Amount(models.Choices):
        """
        成长值规则：
    1. 每签到一次，增加1点成长值，连续签到一周，奖励5点成长值。
    2. 每分享一次，增加1点成长值。
    3. 每成功推荐一名新用户，奖励10点成长值，通过推荐码进行实名注册的新用户视为成功推荐。
    4. 每成功购买一次或挂单成功交易，增加5点成长值。
    5. 每囤茶100g，增加1点成长值，不足100g不能增加成长值。
    6. 挂单价为囤茶价1倍以上，并成功交易，每翻一倍，奖励20点成长值。
        """
        REG = 0
        SIGN = 1  # "签到"
        TRADE = 5  # "交易"
        TUN = 1  # >100 每囤茶100g
        SHARE = 1  # "分享"
        INVITE = 10  # "邀请（推荐）"
        KEEP_SIGN_REWARD = 5  # "连续签到奖励"
        MO_MULTI_PRICE = 20  # "挂单价为1倍以上"

    user = models.ForeignKey(User, verbose_name='用户')
    happen_type = models.CharField("发生类型", choices=HappenType.get_choices())
    amount = models.IntegerField("成长值")
    keep_sign_times = models.IntegerField("连续签到次数", default=0)
    mo_price_multi_n = models.IntegerField("挂单价为囤茶价倍数", default=0)

    @property
    def sign_num(self):
        import datetime
        import time
        num = 1
        dt1 = datetime.datetime.utcfromtimestamp(time.time())
        dt2 = datetime.datetime.utcfromtimestamp(self.add_timestamp)
        day = (dt1 - dt2).days
        if day > 0:
            num = self.keep_sign_times + 1
        return num

    @classmethod
    def is_grow_record(cls, user_id=None, market_order=None):
        import datetime, time
        from apps.product.models import MarketOrder
        """
        是否积分
        翻倍增长 每天只记录前三次交易
        赠送、指定交易不记录
        :param user_id: 当前积分用户
        :param market_order: 交易挂单
        :return:
        """
        if user_id:
            today = datetime.datetime.now().date()
            today = time.mktime(today.timetuple())
            now = time.time()

            grow_count = GrowCoinRecord.objects.filter(user_id=user_id,
                                                       happen_type=GrowCoinRecord.HappenType.MO_MULTI_PRICE.name,
                                                       add_timestamp__range=(today, now)).count()
            if grow_count >= 3:
                return False

        if market_order:
            if market_order.publish_type == MarketOrder.PublishType.DIRECTIONAL.name:
                return False

        return True


class UserTunLevelRight(models.Model):
    """
成长值说明：
1. 成长值是囤个茶用户通过交易消费、签到等获得的，成长值的累计总额决定会员等级
2. 成长值有什么作用？
成长值达到相应等级，即可享受该等级对应的权益，成长值约高，获得的权益越丰厚。
3. 怎样才能增加成长值？
每次参与签到、交易、分享、推荐新用户、获得更多的收益都可以增加成长值。

成长值规则：
1. 每签到一次，增加1点成长值，连续签到一周，奖励5点成长值。
2. 每分享一次，增加1点成长值。
3. 每成功推荐一名新用户，奖励10点成长值，通过推荐码进行实名注册的新用户视为成功推荐。
4. 每成功购买一次或挂单成功交易，增加5点成长值。
5. 每囤茶100g，增加1点成长值，不足100g不能增加成长值。
6. 挂单价为囤茶价1倍以上，并成功交易，每翻一倍，奖励20点成长值。

小白菜囤家：成长值小于50点

V1灰铁囤家：成长值满50点
             1. 每年提货包邮一次

V2青铜囤家：成长值满200点
             1. 每年提货包邮三次
             2. 免费获得试饮装一份

V3白银囤家：成长值满500点
             1. 每年提货包邮五次
2. 免费获得试饮装两份
3. 每年可免费在线下体验体品茶活动一次

V4黄金囤家：成长值满1000点
               1. 每年提货包邮五次
  2. 免费获得试饮装五份
  3. 每年可免费在线下体验体品茶活动三次
  4. 每笔交易手续费减免0.5%


V5铂金囤家：成长值满2000点
               1. 每年提货包邮六次
  2. 免费获得试饮装八份
  3. 每年可免费在线下体验体品茶活动六次
  4. 每笔交易手续费减免0.5%
  5. 每笔库存费减免5%

V6钻石囤家：成长值满5000点
               1. 每年提货包邮六次
  2. 免费获得试饮装八份
  3. 每年可免费在线下体验体品茶活动六次
  4. 每笔交易手续费减免1%
  5. 每笔库存费减免10%

V7最强囤主：成长值满20000点
1. 每年提货包邮十次
             2. 免费获得试饮装十份
3. 每年可免费在线下体验体品茶活动十次
4. 每笔交易手续费减免1%
5. 每笔库存费减免15%
6. 每年平台首发产品有两次优先囤茶权（提前1小时）


V8殿堂囤主：成长值满50000点
  1. 提货永久包邮
  2. 每一款首发产品，均可免费获得试饮装1份
  3. 每年可免费在线下体验体品茶活动十次
  4. 每笔交易手续费减免1%
  5. 每笔库存费减免20%
  6. 每年平台首发产品有五次优先囤茶权（提前1小时）
    """

    class Meta:
        verbose_name = verbose_name_plural = '用户囤级权益'

    tun_level = models.ForeignKey(TunLevel)
    user = models.ForeignKey(User, verbose_name='用户')
    take_out_free_postage_times_1y = models.IntegerField(verbose_name='每年提货包邮次数(负数代表永久)', default=0)
    free_get_try_drink_count = models.IntegerField(verbose_name='免费获得试饮装份数', default=0)
    free_offline_experience_times_1y = models.IntegerField(verbose_name='每年免费线下体验次数', default=0)
    first_publish_product_pre_buy_times_1y = models.IntegerField(verbose_name='每年首发产品优先囤茶次数', default=0)
    procedure_preferential_percent = models.DecimalField("手续费减免比例(EG:0.005)", default=Decimal("0.000"), max_digits=10,
                                                         decimal_places=3)
    repo_preferential_percent = models.DecimalField("仓储费减免比例(EG:0.015)", default=Decimal("0.000"), max_digits=10,
                                                    decimal_places=3)


# class UserTunLevelRightUseRecord(models.Model):
#     """用户囤级权益使用记录
#     """
#
#     class Meta:
#         verbose_name = verbose_name_plural = '用户权益享受记录'
#
#     user = models.ForeignKey(User, verbose_name='用户')
#     use_datetime = models.DateTimeField('使用时间')
#     times = models.IntegerField()


class UserTunLevelRightTimes(models.Model):
    class Right(models.Choices):
        TAKE_OUT_FREE_POSTAGE = '提货包邮'
        FREE_GET_TRY_DRINK = '免费获得试饮装'
        FREE_OFFLINE_EXPERIENCE = '免费线下体验'
        FIRST_PUBLISH_PRODUCT_PRE_BUY = '首发产品优先囤茶'

    user = models.ForeignKey(User, verbose_name='用户')
    right = models.CharField(verbose_name='权益', choices=Right.get_choices())
    expire_at = models.BigIntegerField(verbose_name='有效期至')
    used = models.BooleanField(verbose_name='已使用', default=False)


class UserBalanceRecord(models.Model):
    """
    用户余额变动记录
    """
    class Meta:
        verbose_name = verbose_name_plural = '余额变动记录'
        ordering = ('add_timestamp', )

    class CreateType(models.Choices):
        RECHARGE = '充值'  #
        BUY_PRE_SALE = '购买预售支出'  # # OrderPayView Balance Pay
        MARKET_SELL_OUT = '茶市卖出订单收入'  # # OrderPayView Balance Pay
        MARKET_BUY_IN = '茶市买入订单支出'  # # OrderPayView Balance Pay
        TAKE_OUT_REPO_COST_PAY = '提仓订单支付支出'  # # OrderPayView Balance Pay
        GIFT_REPO_COST_PAY = '赠送订单支付支付支出'  # OrderPayView Balance Pay
        GIFT_REPO_COST_REFUND = '赠送超时存仓费退回收入'  # task_clean_timeout_gift_unreceive_order
        WITHDRAW_CASH = '提现支出'  # task_withdraw_to_alipay_account
        PRE_SALE_FAILED_REFUND = "预售失败退款"  # task_pre_sale_failed_refund

    class RechargeMethod(models.Choices):
        OFFLINE = '线下打款'
        OFFLINE_FOR_DISPATCH = '线下打款（分发）'
        ALIPAY = '支付宝'
        WXPAY = '微信支付'

    user = models.ForeignKey(User, verbose_name='用户')
    create_type = models.CharField('创建类型', choices=CreateType.get_choices())
    amount = models.DecimalField('金额')
    order_id = models.CharField(verbose_name='订单ID', blank=True, null=True)
    recharge_method = models.CharField('充值方式', choices=RechargeMethod.get_choices(),
                                       default=RechargeMethod.OFFLINE.name)
    memo = models.TextField('备注')

    def get_date_line(self):
        import datetime, time
        timeArray = time.localtime(self.add_timestamp)
        add_month = time.strftime("%m", timeArray)
        add_year = time.strftime("%Y", timeArray)
        now_year = str(datetime.datetime.now().year)
        if now_year == add_year:
            return add_month + "月"
        else:
            return add_year + "年" + add_month + "月"

    def get_detail(self):
        import time
        timeArray = time.localtime(self.add_timestamp)
        show_time = time.strftime("%m-%d %H:%M", timeArray)

        return {
            "create_type": self.create_type,
            "create_type_map": dict(UserBalanceRecord.CreateType.get_choices()),
            "amount": self.amount,
            "recharge_method": self.recharge_method,
            "recharge_method_map": dict(UserBalanceRecord.RechargeMethod.get_choices()),
            "memo": self.memo,
            "add_timestamp": show_time,
        }

class UserToken(object):
    """
    Token处理
    """

    @classmethod
    def update_token(cls, user_id, token):
        from tuntun.settings import REDIS
        from sdks.mredis import String
        robj = String('%s:%s:%s' % (REDIS['ENV_LABEL'], REDIS['TOKEN_PREFIX'], user_id))
        robj.set(token)
        return token

    @classmethod
    def get_token(cls, user_id):
        from tuntun.settings import REDIS
        from sdks.mredis import String
        key = '%s:%s:%s' % (REDIS['ENV_LABEL'], REDIS['TOKEN_PREFIX'], user_id)
        # print(key)
        robj = String(key)
        return robj.get()